#include <ysbuild.h>
#include YFM_Helper_GUIApplication
#include YFM_Helper_HostWindow
#include <iostream>
#include "../HelperEx/Shells.h"


using namespace std;
using namespace YSLib;
using namespace YSLib::Drawing;
using UI::Widget;
using UI::Button;
using UI::GAnimationSession;
using UI::InvalidationUpdater;
using Host::Window;
using Host::NativeWindowHandle;
using Host::HostRenderer;


namespace
{

//! \since YSLib build 451
//@{
static yconstexpr auto PI = 3.14159265358979323;
static yconstexpr auto PI_2 = PI * 2;
static yconstexpr auto PI_4 = PI * 4;
//@}

//! \since YSLib build 912
void
DrawStar(Graphics& g, Color c, const Point& pt, SDst r, double a, size_t n = 5)
{
	std::vector<Point> pts(n);

	for(size_t i = 0; i < n; ++i)
		pts[i] = {int(-std::cos(PI_4 / n * i + a) * r + pt.X),
			int(std::sin(PI_4 / n * i + a) * r + pt.Y)};
	DrawPolygon(g, g.GetSize(), pts.cbegin(), pts.cend(), c);
}

//! \since YSLib build 449
template<typename... _tParams>
shared_ptr<tuple<_tParams...>>
make_shared_tuple(const _tParams&... args)
{
	return ystdex::make_shared<tuple<_tParams...>>(make_tuple(args...));
}

} // unnamed namespace;


#if 0
template<typename... _tParams>
void
CallByReference(_tParams&&... args)
{
	PostTask([&]{
		if(Update(yforward(args)...))
			CallByValue(yforward(args)...));
	}, TaskPriority);
}

template<typename... _tParams>
void
CallByValue(_tParams&&... args)
{
	PostTask([=]{
		if(Update(yforward(args)...))
			CallByValue(yforward(args)...);
	}, TaskPriority);
}

template<typename _tShell, typename _fCallable>
void
WaitForShell(_fCallable f)
{
	PostTask([=]{
		if(const auto p = dynamic_cast<_tShell*>(FetchShellHandle().get()))
			f(*p);
		else
			WaitForShell<_tShell>(f);
	}, TaskPriority);
}
#endif


#define YTest_Delay 0
#define YTest_Ani 2

class AniWnd : public UI::Window
{
public:
	static yconstexpr const double FPS = 116;

protected:
	FPSCounter fpsCounter{std::chrono::milliseconds(500)};

private:
#if YTest_Delay
	Timers::Timer tmr{std::chrono::microseconds{u64(1000000 / FPS)}};
#	define YTest_IfTimeRefresh if(tmr.Refresh())
#else
#	define YTest_IfTimeRefresh
#endif
	double rad = 0;
	GAnimationSession<InvalidationUpdater> ani;

public:
	AniWnd(const Rect& r)
		: UI::Window(r)
	{
		using namespace UI;

		yunseq(
		Background = SolidBrush(ColorSpace::Yellow),
		FetchEvent<Paint>(*this) += [this](PaintEventArgs&& e){
			DrawStar(e.Target, ColorSpace::Red, {300, 200}, 100, rad),
			DrawStar(e.Target, ColorSpace::Green, {300, 200}, 100, rad + PI);
			rad += 0.02;
			if(rad > PI_2)
				rad -= PI_2;
		}
		);
		ani.Reset(make_observer(this), true);
#if YTest_Ani == 2
		ani.GetConnectionRef().Invalidate = [this](IWidget& wgt){
			YTest_IfTimeRefresh
			{
				Host::PrintFPS(fpsCounter, *Host::GetWindowPtrOf(*this));
				InvalidateVisible(wgt);
			}
			return true;
		};
		ani.Start();
#if !YTest_Delay
		PostTask([]{
			dynamic_cast<Shells::GUIShell&>(
				*FetchShellHandle()).IdleSleep = std::chrono::nanoseconds(100);
		});
#endif
#elif YTest_Ani == 1
		UI::AnimateConnection(
			ystdex::make_shared<InvalidationUpdater>(this, true));

#else
		UI::Animate([this]{
			YTest_IfTimeRefresh
			{
				Host::PrintFPS(fpsCounter, *Host::GetWindowPtrOf(*this));
				Invalidate(*this, {200, 100, 200, 200});
			//	return ani.GetConnectionRef()();
			}
			return true;
		});
#endif
	}
};


int
main()
{
	using namespace YSLib;

	GUIApplication app;
	AniWnd wgt(Rect(0, 0, 640, 480));

	Host::ShowTopLevel(wgt, WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, 0);

//	IdleSleep = {};
	Execute(app);
}

